package org.osll.tictactoe.transport.udp.server;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

import org.osll.tictactoe.Move;
import org.osll.tictactoe.engine.MoveImpl;
import org.osll.tictactoe.game.Game;
import org.osll.tictactoe.transport.tcp.ApponentMoveQuery;
import org.osll.tictactoe.transport.tcp.ApponentMoveResponse;
import org.osll.tictactoe.transport.tcp.ApponentNameQuery;
import org.osll.tictactoe.transport.tcp.ApponentNameResponse;
import org.osll.tictactoe.transport.tcp.DefaultQuery;
import org.osll.tictactoe.transport.tcp.ErrorResponse;
import org.osll.tictactoe.transport.tcp.LoginAcceptedResponse;
import org.osll.tictactoe.transport.tcp.LoginQuery;
import org.osll.tictactoe.transport.tcp.LoginRejectedResponse;
import org.osll.tictactoe.transport.tcp.MoveQuery;
import org.osll.tictactoe.transport.tcp.MoveResponse;
import org.osll.tictactoe.transport.tcp.OverResponse;
import org.osll.tictactoe.transport.tcp.ReflexiveTimeResponse;
import org.osll.tictactoe.transport.tcp.SizeResponse;
import org.osll.tictactoe.transport.tcp.StartedResponse;
import org.osll.tictactoe.transport.udp.SocketProcessor;

public class GameServer extends SocketProcessor implements Runnable {

	private DatagramSocket socket = null;
	private Game controller = null;
	
	public GameServer(Game controller) {
		this.controller = controller;
		try {
			socket = new DatagramSocket(0);
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	@Override
	public void run() {
		for(;;) {
			byte [] buf = new byte[30000];
			DatagramPacket packet = new DatagramPacket(buf,buf.length);
			try {
				socket.receive(packet);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				continue;
			}
			
			DefaultQuery query = (DefaultQuery)read(packet);
			
			switch (query.getType()) {
			case LOGIN:
			{
				LoginAcceptedResponse resp = new LoginAcceptedResponse();
				LoginQuery q = (LoginQuery)query;
				try {
					controller.loginPlayer(q.getName(), q.getTeam());
				} catch (IllegalStateException e) {
					write(socket, packet.getSocketAddress(), new LoginRejectedResponse());
				}
				write(socket,packet.getSocketAddress(), resp);
				break;
			}
			case SIZE:
			{
				SizeResponse resp = new SizeResponse();
				resp.setSize(controller.getField().getSize());
				write(socket,packet.getSocketAddress(),resp);
				break;
			}
			case REFLEXIVE_TIME:
			{
				ReflexiveTimeResponse resp = new ReflexiveTimeResponse();
				resp.setMove(controller.getCurrentMove());
				write(socket,packet.getSocketAddress(),resp);
				break;
			}
			case MOVE:
			{
				MoveQuery q = (MoveQuery)query;
				Move move = new MoveImpl(q.getRow(),q.getCol(),q.getTeam());
				try {
					controller.makeMove(move);
					write(socket,packet.getSocketAddress(),new MoveResponse());
				} catch (IllegalStateException e) {
					ErrorResponse r = new ErrorResponse();
					r.setException(e);
					write(socket,packet.getSocketAddress(),r);
				}
				break;
			}
			case APPONENT_MOVE:
			{
				ApponentMoveResponse resp = new ApponentMoveResponse();
				ApponentMoveQuery q = (ApponentMoveQuery)query;
				try {
					resp.setMove(controller.getLastMove(q.getTeam()));
					write(socket,packet.getSocketAddress(),resp);
				} catch(IllegalStateException e) {
					ErrorResponse r = new ErrorResponse();
					r.setException(e);
					write(socket,packet.getSocketAddress(),r);
				}
				break;
			}
			case STARTED: 
			{
				StartedResponse resp = new StartedResponse();
				resp.setStarted(controller.isStarted());
				write(socket,packet.getSocketAddress(),resp);
				break;
			}
			case OVER:
			{
				OverResponse resp = new OverResponse();
				resp.setOver(controller.isOver());
				write(socket,packet.getSocketAddress(),resp);
				break;
			}
			case APPONENT_NAME:
			{
				ApponentNameResponse resp = new ApponentNameResponse();
				resp.setName(controller.getApponentName(((ApponentNameQuery)query).getTeam()));
				write(socket,packet.getSocketAddress(),resp);
				break;
			}
			default:
				break;
			}
			
		}
	}

	public int getPort() {
		return socket.getLocalPort();
	}
}
